#include "XMLHandler.h"
#include "Tags.h"
#include "Draft.h"


XMLHandler::XMLHandler(DraftPtr draft)
: _draft(draft)
, _errorString("No error")
{
    CreateParentsDict();
    CreateAttrsDict();
}

bool XMLHandler::CheckParent(const QString& tag, const QString& parent)
{
    return (parent == _parentsDict.value(tag, "unknown")) || SetError(QString("\"%1\" must have \"%2\" as parent").arg(tag, _parentsDict.value(tag)));
}

bool XMLHandler::CheckAttrs(const QString& tag, const StringSet& attrNames)
{
    return _attrsDict.contains(tag, attrNames) || SetError(QString("\"%1\" has invalid attribute names").arg(tag));
}

bool XMLHandler::OnQualifier(const QString& parent, const QString& tag, const QXmlAttributes& attributes)
{
    StringMap attrPairs;

    for (int index = 0; index != attributes.count(); ++index)
        attrPairs.insert(attributes.qName(index), attributes.value(index));

    return CheckParent(tag, parent) && CheckAttrs(tag, StringSet::fromList(attrPairs.keys())) && _draft->OnTag(tag, attrPairs); 
}

bool XMLHandler::startElement(const QString& /* namespaceURI */,
                              const QString& /* localName */,
                              const QString& qName,
                              const QXmlAttributes& attributes)
{
    QString top = _tags.top();
    _tags.push(qName);
    return OnQualifier(top, qName, attributes);
}

bool XMLHandler::endElement(const QString& /* namespaceURI */,
                            const QString& /* localName */,
                            const QString& qName)
{
    return _tags.pop() == qName;
}

bool XMLHandler::characters(const QString& str)
{
    return true;
}

bool XMLHandler::fatalError(const QXmlParseException& exception)
{
    return SetError(QString("Parse error at line %1, column %2: %3. Processing tag \"%4\".")
            .arg(exception.lineNumber())
            .arg(exception.columnNumber())
            .arg(exception.message())
            .arg(_tags.top()));
}

bool XMLHandler::SetError(const QString& error)
{
    _errorString = error;
    return false;
}

QString XMLHandler::GetError()
{
    return _errorString;
}

void XMLHandler::CreateParentsDict()
{
    _parentsDict[tag::QMachine]     = tag::Void;

    _parentsDict[tag::QChannel]     = tag::QMachine;
    _parentsDict[tag::QProperty]    = tag::QChannel;

    _parentsDict[tag::QGlobal]      = tag::QMachine;
    _parentsDict[tag::QInitial]     = tag::QMachine;
    _parentsDict[tag::QState]       = tag::QMachine;
    
    _parentsDict[tag::QProcedure]   = tag::QState;
    _parentsDict[tag::QTransfer]    = tag::QState;
   
    _parentsDict[tag::QAction]      = tag::QTransfer;
    _parentsDict[tag::QChar]        = tag::QTransfer;
}

void XMLHandler::CreateAttrsDict()
{
    _attrsDict.insert(tag::QMachine , StringSet());

    _attrsDict.insert(tag::QChannel , StringSet() << tag::AName << tag::AType);
    _attrsDict.insert(tag::QProperty, StringSet() << tag::AName << tag::AValue);

    _attrsDict.insert(tag::QGlobal  , StringSet() << tag::AName << tag::AValue);
    _attrsDict.insert(tag::QInitial , StringSet() << tag::AName);
    _attrsDict.insert(tag::QState   , StringSet() << tag::AName);

    _attrsDict.insert(tag::QProcedure, StringSet() << tag::AAction << tag::AObject);
    _attrsDict.insert(tag::QTransfer, StringSet() << tag::ADest);
    
    _attrsDict.insert(tag::QAction  , StringSet() << tag::AAction << tag::AObject);
    _attrsDict.insert(tag::QAction  , StringSet() << tag::AAction << tag::AObject << tag::AChannel);

    _attrsDict.insert(tag::QChar    , StringSet() << tag::AValue);
}

bool XMLHandler::startDocument()
{
    return _draft ? (_tags.push(tag::Void), true) : SetError("Draft = NULL");
}

bool XMLHandler::endDocument()
{
    return _draft->isValid() || SetError(_draft->GetValidationErrors());
}